use bevy::core_pipeline::clear_color::ClearColorConfig;
use bevy::prelude::*;
use std::f32::consts::PI;
use std::time::SystemTime;

use crate::data::*;

pub struct StartUpPlugin;

impl Plugin for StartUpPlugin {
    fn build(&self, app: &mut App) {
        // Startup
        app.insert_resource(FieldData::default())
            .insert_resource(Statistics::default())
            .add_systems(Startup, setup_command)
            .add_systems(Startup, setup_graphics)
            .add_systems(Startup, (spawn_planes, spawn_lines, spawn_gates))
            .add_systems(Startup, spawn_robot);
    }
}

pub fn setup_command(mut commands: Commands) {
    commands.spawn((
        GameStatus::Ready {
            start_time: SystemTime::now(),
        },
        GameStatusChanged,
    ));
}

pub fn setup_graphics(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    field_data: Res<FieldData>,
) {
    let FieldData {
        field_length,
        field_width,
        ..
    } = field_data.to_owned();
    // Camera
    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(0.0, 50.0, 6.0).looking_at(Vec3::ZERO, Vec3::Y),
            camera_3d: Camera3d {
                clear_color: ClearColorConfig::Custom(Color::rgb_u8(0x99, 0x99, 0xff)),
                ..default()
            },
            ..default()
        },
        CameraTrackingTranslation {
            translation: Vec3::Y,
            rotate_angle: 0.0,
        },
    ));
    // light
    commands.spawn(DirectionalLightBundle {
        directional_light: DirectionalLight {
            // color: Color::rgb(1.0, 1.0, 0.9),
            shadows_enabled: true,
            illuminance: 10000.0,
            ..default()
        },
        transform: Transform::from_xyz(6.0, 20.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
    // 球
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(
                shape::UVSphere {
                    radius: BALL_RADIUS,
                    ..Default::default()
                }
                .into(),
            ),
            material: materials.add(Color::YELLOW.into()),
            transform: Transform::from_xyz(-field_length / 2., BALL_RADIUS, field_width / 2.),
            ..default()
        },
        Ball,
        HorizontalMovingStatus::default(),
    ));
    // 提示文本
    commands.spawn((
        TextBundle::from_section(
            "",
            TextStyle {
                font_size: 20.,
                ..default()
            },
        )
        .with_style(Style {
            position_type: PositionType::Absolute,
            top: Val::Px(12.0),
            left: Val::Px(12.0),
            ..default()
        }),
        TipsText,
    ));
    // 提示文本
    commands.spawn((
        TextBundle::from_section(
            "",
            TextStyle {
                font_size: 20.,
                ..default()
            },
        )
        .with_style(Style {
            position_type: PositionType::Absolute,
            top: Val::Px(12.0),
            right: Val::Px(12.0),
            ..default()
        }),
        StatisticsText,
    ));
}

pub fn spawn_planes(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    field_data: Res<FieldData>,
) {
    let FieldData { field_color, .. } = field_data.to_owned();
    // 白底
    commands.spawn(PbrBundle {
        mesh: meshes.add(Mesh::from(shape::Quad::flipped(
            [18.0 + 10.0, 12.0 + 10.0].into(),
        ))),
        material: materials.add(Color::WHITE.into()),
        transform: Transform::from_rotation(Quat::from_rotation_x(-PI / 2.))
            .with_translation([0.0, -0.001, 0.0].into()),
        ..default()
    });
    // 绿色场地
    commands.spawn(PbrBundle {
        mesh: meshes.add(Mesh::from(shape::Quad::flipped(
            [18.0 + 5.0, 12.0 + 5.0].into(),
        ))),
        material: materials.add(field_color.into()),
        transform: Transform::from_rotation(Quat::from_rotation_x(-PI / 2.))
            .with_translation([0.0, 0.0, 0.0].into()),
        ..default()
    });
}

pub fn spawn_lines(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    field_data: Res<FieldData>,
) {
    let FieldData {
        field_length,
        field_width,
        line_width,
        line_height,
        line_color,
        penalty_area_length,
        penalty_area_width,
        goal_area_length,
        goal_area_width,
        ..
    } = field_data.to_owned();

    // 边线
    let shape_field_length = shape::Box::new(field_length + line_width, line_height, line_width);
    let mesh_field_length = meshes.add(Mesh::from(shape_field_length));
    let shape_field_width = shape::Box::new(line_width, line_height, field_width - line_width);
    let mesh_field_width = meshes.add(Mesh::from(shape_field_width));
    let line_material = materials.add(line_color.into());
    for ratio in [1.0, -1.0] {
        commands.spawn(PbrBundle {
            mesh: mesh_field_length.clone(),
            material: line_material.clone(),
            transform: Transform::from_xyz(0.0, line_height / 2., ratio * field_width / 2.),
            ..default()
        });
        commands.spawn(PbrBundle {
            mesh: mesh_field_width.clone(),
            material: line_material.clone(),
            transform: Transform::from_xyz(ratio * field_length / 2., line_height / 2., 0.0),
            ..default()
        });
    }
    // 中线
    commands.spawn(PbrBundle {
        mesh: mesh_field_width.clone(),
        material: line_material.clone(),
        transform: Transform::from_xyz(0.0, line_height / 2., 0.0),
        ..default()
    });
    // 大禁区
    spawn_penalty(
        &mut commands,
        &mut meshes,
        line_material.clone(),
        field_data.to_owned(),
        penalty_area_length,
        penalty_area_width,
    );
    // 小禁区
    spawn_penalty(
        &mut commands,
        &mut meshes,
        line_material.clone(),
        field_data.to_owned(),
        goal_area_length,
        goal_area_width,
    );
    // 中圈
    let mut circle_tran = Transform::from_xyz(0.0, 0.001, 0.0);
    circle_tran.rotate_x(-PI / 2.);
    commands.spawn(PbrBundle {
        mesh: meshes.add(Mesh::from(shape::Circle::new(2.0))),
        material: line_material,
        transform: circle_tran,
        ..default()
    });
}

fn spawn_penalty(
    commands: &mut Commands,
    meshes: &mut ResMut<Assets<Mesh>>,
    line_material: Handle<StandardMaterial>,
    field_data: FieldData,
    penalty_length: f32,
    penalty_width: f32,
) {
    let FieldData {
        field_length,
        line_width,
        line_height,
        ..
    } = field_data.to_owned();
    let shape_penalty_length =
        shape::Box::new(penalty_length + line_width, line_height, line_width);
    let mesh_penalty_length = meshes.add(shape_penalty_length.into());
    let shape_penalty_width = shape::Box::new(line_width, line_height, penalty_width - line_width);
    let mesh_penalty_width = meshes.add(shape_penalty_width.into());
    for ratio in [1.0, -1.0] {
        commands.spawn(PbrBundle {
            mesh: mesh_penalty_width.clone(),
            material: line_material.clone(),
            transform: Transform::from_xyz(
                ratio * ((field_length / 2.) - penalty_length),
                line_height / 2.,
                0.0,
            ),
            ..default()
        });
        let transform_length_1 = Transform::from_xyz(
            ratio * ((field_length / 2.) - (penalty_length / 2.)),
            line_height / 2.,
            penalty_width / 2.,
        );
        let transform_length_2 = Transform::from_xyz(
            ratio * ((field_length / 2.) - (penalty_length / 2.)),
            line_height / 2.,
            -penalty_width / 2.,
        );
        commands.spawn(PbrBundle {
            mesh: mesh_penalty_length.clone(),
            material: line_material.clone(),
            transform: transform_length_1,
            ..default()
        });
        commands.spawn(PbrBundle {
            mesh: mesh_penalty_length.clone(),
            material: line_material.clone(),
            transform: transform_length_2,
            ..default()
        });
    }
}

pub fn spawn_gates(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    field_data: Res<FieldData>,
) {
    let FieldData {
        field_length,
        goal_area_width,
        gate_height,
        gate_depth,
        gate_thickness,
        ..
    } = field_data.to_owned();
    let gate_width_shape = shape::Box::new(
        gate_thickness,
        gate_thickness,
        goal_area_width - gate_thickness,
    );
    let gate_width_mesh = meshes.add(gate_width_shape.into());
    let gate_depth_shape =
        shape::Box::new(gate_depth - gate_thickness, gate_thickness, gate_thickness);
    let gate_depth_mesh = meshes.add(gate_depth_shape.into());
    let gate_height_shape =
        shape::Box::new(gate_thickness, gate_height + gate_thickness, gate_thickness);
    let gate_height_mesh = meshes.add(gate_height_shape.into());
    let gate_color = Color::rgb_u8(0xee, 0xee, 0xee);
    let gate_material = materials.add(gate_color.into());
    for team_ratio in [1.0, -1.0] {
        // Width
        for (depth, height) in [
            (0.0, gate_height),
            (gate_depth, 0.0),
            (gate_depth, gate_height),
        ] {
            commands.spawn(PbrBundle {
                // mesh: meshes.add(Mesh::from(gate_width_shape)),
                mesh: gate_width_mesh.clone(),
                material: gate_material.clone(),
                transform: Transform::from_xyz(
                    team_ratio * (field_length / 2. + depth),
                    height + gate_thickness / 2.,
                    0.0,
                ),
                ..default()
            });
        }
        // Depth
        for (side_ratio, height) in [
            (1.0, 0.0),
            (1.0, gate_height),
            (-1.0, 0.0),
            (-1.0, gate_height),
        ] {
            commands.spawn(PbrBundle {
                // mesh: meshes.add(Mesh::from(gate_depth_shape)),
                mesh: gate_depth_mesh.clone(),
                material: gate_material.clone(),
                transform: Transform::from_xyz(
                    team_ratio * (field_length / 2. + gate_depth / 2.),
                    height + gate_thickness / 2.,
                    side_ratio * (goal_area_width / 2.),
                ),
                ..default()
            });
        }
        // height
        for (side_ratio, depth) in [
            (1.0, 0.0),
            (1.0, gate_depth),
            (-1.0, 0.0),
            (-1.0, gate_depth),
        ] {
            commands.spawn(PbrBundle {
                // mesh: meshes.add(Mesh::from(gate_height_shape)),
                mesh: gate_height_mesh.clone(),
                material: gate_material.clone(),
                transform: Transform::from_xyz(
                    team_ratio * (field_length / 2. + depth),
                    gate_height / 2. + gate_thickness / 2.,
                    side_ratio * (goal_area_width / 2.),
                ),
                ..default()
            });
        }
    }
}

pub fn spawn_robot(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    field_data: Res<FieldData>,
) {
    let FieldData { field_length, .. } = field_data.to_owned();
    // Robot
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(
                shape::Cylinder {
                    height: ROBOT_HEIGHT,
                    radius: ROBOT_RADIUS,
                    ..default()
                }
                .into(),
            ),
            material: materials.add(StandardMaterial {
                base_color: Color::rgba(0.0, 0.0, 0.0, 0.5),
                alpha_mode: AlphaMode::Mask(0.5),
                ..default()
            }),
            transform: Transform::from_xyz(-field_length / 2., ROBOT_HEIGHT / 2., 0.0)
                .looking_at([0.0, ROBOT_HEIGHT / 2., 0.0].into(), Vec3::Y),
            ..default()
        },
        Robot::default(),
        HorizontalMovingStatus::default(),
    ));
}
